home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / emacssrc.zip / EMACSSRC.TAR / emacs-19.17 / lib-src / fakemail.c < prev    next >
C/C++ Source or Header  |  1993-11-16  |  15KB  |  662 lines

  1. /* sendmail-like interface to /bin/mail for system V,
  2.    Copyright (C) 1985 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU Emacs.
  5.  
  6. GNU Emacs is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Emacs is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Emacs; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20.  
  21. #define NO_SHORTNAMES
  22. #include "../src/config.h"
  23.  
  24. #if defined (BSD) && !defined (BSD4_1) && !defined (USE_FAKEMAIL)
  25. /* This program isnot used in BSD, so just avoid loader complaints.  */
  26. main ()
  27. {
  28. }
  29. #else /* not BSD 4.2 (or newer) */
  30. /* This conditional contains all the rest of the file.  */
  31.  
  32. /* These are defined in config in some versions. */
  33.  
  34. #ifdef static
  35. #undef static
  36. #endif
  37.  
  38. #ifdef read
  39. #undef read
  40. #undef write
  41. #undef open
  42. #undef close
  43. #endif
  44.  
  45. #include <stdio.h>
  46. #include <string.h>
  47. #include <ctype.h>
  48. #include <time.h>
  49. #include <pwd.h>
  50.  
  51. /* Type definitions */
  52.  
  53. #define boolean int
  54. #define true 1
  55. #define false 0
  56.  
  57. /* Various lists */
  58.  
  59. struct line_record
  60. {
  61.   char *string;
  62.   struct line_record *continuation;
  63. };
  64. typedef struct line_record *line_list;
  65.  
  66. struct header_record
  67. {
  68.   line_list text;
  69.   struct header_record *next;
  70.   struct header_record *previous;
  71. };
  72. typedef struct header_record *header;
  73.             
  74. struct stream_record
  75. {
  76.   FILE *handle;
  77.   int (*action)();
  78.   struct stream_record *rest_streams;
  79. };
  80. typedef struct stream_record *stream_list;
  81.  
  82. /* A `struct linebuffer' is a structure which holds a line of text.
  83.  * `readline' reads a line from a stream into a linebuffer
  84.  * and works regardless of the length of the line.
  85.  */
  86.  
  87. struct linebuffer
  88. {
  89.   long size;
  90.   char *buffer;
  91. };
  92.  
  93. struct linebuffer lb;
  94.  
  95. #define new_list()                    \
  96.   ((line_list) xmalloc (sizeof (struct line_record)))
  97. #define new_header()                \
  98.   ((header) xmalloc (sizeof (struct header_record)))
  99. #define new_stream()                \
  100.   ((stream_list) xmalloc (sizeof (struct stream_record)))
  101. #define alloc_string(nchars)                \
  102.   ((char *) xmalloc ((nchars) + 1))
  103.  
  104. /* Global declarations */
  105.  
  106. #define BUFLEN 1024
  107. #define KEYWORD_SIZE 256
  108. #define FROM_PREFIX "From"
  109. #define MY_NAME "fakemail"
  110. #define NIL ((line_list) NULL)
  111. #define INITIAL_LINE_SIZE 200
  112.  
  113. #ifndef MAIL_PROGRAM_NAME
  114. #define MAIL_PROGRAM_NAME "/bin/mail"
  115. #endif
  116.  
  117. static char *my_name;
  118. static char *the_date;
  119. static char *the_user;
  120. static line_list file_preface;
  121. static stream_list the_streams;
  122. static boolean no_problems = true;
  123.  
  124. extern FILE *popen ();
  125. extern int fclose (), pclose ();
  126. extern char *malloc (), *realloc ();
  127.  
  128. #ifdef CURRENT_USER
  129. extern struct passwd *getpwuid ();
  130. extern unsigned short geteuid ();
  131. static struct passwd *my_entry;
  132. #define cuserid(s)                \
  133. (my_entry = getpwuid (((int) geteuid ())),    \
  134.  my_entry->pw_name)
  135. #endif
  136.  
  137. /* Utilities */
  138.  
  139. /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
  140.  
  141. static void
  142. error (s1, s2)
  143.      char *s1, *s2;
  144. {
  145.   printf ("%s: ", my_name);
  146.   printf (s1, s2);
  147.   printf ("\n");
  148.   no_problems = false;
  149. }
  150.  
  151. /* Print error message and exit.  */
  152.  
  153. static void
  154. fatal (s1, s2)
  155.      char *s1, *s2;
  156. {
  157.   error (s1, s2);
  158.   exit (1);
  159. }
  160.  
  161. /* Like malloc but get fatal error if memory is exhausted.  */
  162.  
  163. static char *
  164. xmalloc (size)
  165.      int size;
  166. {
  167.   char *result = malloc (((unsigned) size));
  168.   if (result == ((char *) NULL))
  169.     fatal ("virtual memory exhausted", 0);
  170.   return result;
  171. }
  172.  
  173. static char *
  174. xrealloc (ptr, size)
  175.      char *ptr;
  176.      int size;
  177. {
  178.   char *result = realloc (ptr, ((unsigned) size));
  179.   if (result == ((char *) NULL))
  180.     fatal ("virtual memory exhausted");
  181.   return result;
  182. }
  183.  
  184. /* Initialize a linebuffer for use */
  185.  
  186. void
  187. init_linebuffer (linebuffer)
  188.      struct linebuffer *linebuffer;
  189. {
  190.   linebuffer->size = INITIAL_LINE_SIZE;
  191.   linebuffer->buffer = ((char *) xmalloc (INITIAL_LINE_SIZE));
  192. }
  193.  
  194. /* Read a line of text from `stream' into `linebuffer'.
  195.  * Return the length of the line.  
  196.  */
  197.  
  198. long
  199. readline (linebuffer, stream)
  200.      struct linebuffer *linebuffer;
  201.      FILE *stream;
  202. {
  203.   char *buffer = linebuffer->buffer;
  204.   char *p = linebuffer->buffer;
  205.   char *end = p + linebuffer->size;
  206.  
  207.   while (true)
  208.     {
  209.       int c = getc (stream);
  210.       if (p == end)
  211.     {
  212.       linebuffer->size *= 2;
  213.       buffer = ((char *) xrealloc (buffer, linebuffer->size));
  214.       p += buffer - linebuffer->buffer;
  215.       end += buffer - linebuffer->buffer;
  216.       linebuffer->buffer = buffer;
  217.     }
  218.       if (c < 0 || c == '\n')
  219.     {
  220.       *p = 0;
  221.       break;
  222.     }
  223.       *p++ = c;
  224.     }
  225.  
  226.   return p - buffer;
  227. }
  228.  
  229. char *
  230. get_keyword (field, rest)
  231.      register char *field;
  232.      char **rest;
  233. {
  234.   static char keyword[KEYWORD_SIZE];
  235.   register char *ptr;
  236.   register char c;
  237.  
  238.   ptr = &keyword[0];
  239.   c = *field++;
  240.   if ((isspace (c)) || (c == ':'))
  241.     return ((char *) NULL);
  242.   *ptr++ = ((islower (c)) ? (toupper (c)) : c);
  243.   while (((c = *field++) != ':') && (!(isspace (c))))
  244.     *ptr++ = ((islower (c)) ? (toupper (c)) : c);
  245.   *ptr++ = '\0';
  246.   while (isspace (c)) c = *field++;
  247.   if (c != ':') return ((char *) NULL);
  248.   *rest = field;
  249.   return &keyword[0];
  250. }
  251.  
  252. boolean
  253. has_keyword (field)
  254.      char *field;
  255. {
  256.   char *ignored;
  257.   return (get_keyword (field, &ignored) != ((char *) NULL));
  258. }
  259.  
  260. char *
  261. add_field (the_list, field, where)
  262.      line_list the_list;
  263.      register char *field, *where;
  264. {
  265.   register char c;
  266.   while (true)
  267.     {
  268.       *where++ = ' ';
  269.       while ((c = *field++) != '\0')
  270.     {
  271.       if (c == '(')
  272.         {
  273.           while (*field && *field != ')') ++field;
  274.           if (! (*field++)) break; /* no closer */
  275.           if (! (*field))   break; /* closerNULL */
  276.           c = *field;
  277.         }
  278.       *where++ = ((c == ','||c=='>'||c=='<') ? ' ' : c);
  279.     }
  280.       if (the_list == NIL) break;
  281.       field = the_list->string;
  282.       the_list = the_list->continuation;
  283.     }
  284.   return where;
  285. }
  286.  
  287. line_list
  288. make_file_preface ()
  289. {
  290.   char *the_string, *temp;
  291.   long idiotic_interface;
  292.   long prefix_length;
  293.   long user_length;
  294.   long date_length;
  295.   line_list result;
  296.  
  297.   prefix_length = strlen (FROM_PREFIX);
  298.   time (&idiotic_interface);
  299.   the_date = ctime (&idiotic_interface);
  300.   /* the_date has an unwanted newline at the end */
  301.   date_length = strlen (the_date) - 1;
  302.   the_date[date_length] = '\0';
  303.   temp = cuserid ((char *) NULL);
  304.   user_length = strlen (temp);
  305.   the_user = alloc_string (user_length + 1);
  306.   strcpy (the_user, temp);
  307.   the_string = alloc_string (3 + prefix_length +
  308.                  user_length +
  309.                  date_length);
  310.   temp = the_string;
  311.   strcpy (temp, FROM_PREFIX);
  312.   temp = &temp[prefix_length];
  313.   *temp++ = ' ';
  314.   strcpy (temp, the_user);
  315.   temp = &temp[user_length];
  316.   *temp++ = ' ';
  317.   strcpy (temp, the_date);
  318.   result = new_list ();
  319.   result->string = the_string;
  320.   result->continuation = ((line_list) NULL);
  321.   return result;
  322. }
  323.  
  324. void
  325. write_line_list (the_list, the_stream)
  326.      register line_list the_list;
  327.      FILE *the_stream;
  328. {
  329.   for ( ;
  330.       the_list != ((line_list) NULL) ;
  331.       the_list = the_list->continuation)
  332.     {
  333.       fputs (the_list->string, the_stream);
  334.       putc ('\n', the_stream);
  335.     }
  336.   return;
  337. }
  338.  
  339. int
  340. close_the_streams ()
  341. {
  342.   register stream_list rem;
  343.   for (rem = the_streams;
  344.        rem != ((stream_list) NULL);
  345.        rem = rem->rest_streams)
  346.     no_problems = (no_problems &&
  347.            ((*rem->action) (rem->handle) == 0));
  348.   the_streams = ((stream_list) NULL);
  349.   return (no_problems ? 0 : 1);
  350. }
  351.  
  352. void
  353. add_a_stream (the_stream, closing_action)
  354.      FILE *the_stream;
  355.      int (*closing_action)();
  356. {
  357.   stream_list old = the_streams;
  358.   the_streams = new_stream ();
  359.   the_streams->handle = the_stream;
  360.   the_streams->action = closing_action;
  361.   the_streams->rest_streams = old;
  362.   return;
  363. }
  364.  
  365. int
  366. my_fclose (the_file)
  367.      FILE *the_file;
  368. {
  369.   putc ('\n', the_file);
  370.   fflush (the_file);
  371.   return fclose (the_file);
  372. }
  373.  
  374. boolean
  375. open_a_file (name)
  376.      char *name;
  377. {
  378.   FILE *the_stream = fopen (name, "a");
  379.   if (the_stream != ((FILE *) NULL))
  380.     {
  381.       add_a_stream (the_stream, my_fclose);
  382.       if (the_user == ((char *) NULL))
  383.     file_preface = make_file_preface ();
  384.       write_line_list (file_preface, the_stream);
  385.       return true;
  386.     }
  387.   return false;
  388. }
  389.  
  390. void
  391. put_string (s)
  392.      char *s;
  393. {
  394.   register stream_list rem;
  395.   for (rem = the_streams;
  396.        rem != ((stream_list) NULL);
  397.        rem = rem->rest_streams)
  398.     fputs (s, rem->handle);
  399.   return;
  400. }
  401.  
  402. void
  403. put_line (string)
  404.      char *string;
  405. {
  406.   register stream_list rem;
  407.   for (rem = the_streams;
  408.        rem != ((stream_list) NULL);
  409.        rem = rem->rest_streams)
  410.     {
  411.       char *s = string;
  412.       int column = 0;
  413.  
  414.       /* Divide STRING into lines.  */
  415.       while (*s != 0)
  416.     {
  417.       char *breakpos;
  418.  
  419.       /* Find the last char that fits. */
  420.       for (breakpos = s; *breakpos && column < 78; ++breakpos)
  421.         {
  422.           if (*breakpos == '\t')
  423.         column += 8;
  424.           else
  425.         column++;
  426.         }
  427.       /* Back up to just after the last comma that fits.  */
  428.       while (breakpos != s && breakpos[-1] != ',') --breakpos;
  429.       if (breakpos == s)
  430.         {
  431.           /* If no comma fits, move past the first address anyway.  */
  432.           while (*breakpos != 0 && *breakpos != ',') ++breakpos;
  433.           if (*breakpos != 0)
  434.         /* Include the comma after it.  */
  435.         ++breakpos;
  436.         }
  437.       /* Output that much, then break the line.  */
  438.       fwrite (s, 1, breakpos - s, rem->handle);
  439.       column = 8;
  440.  
  441.       /* Skip whitespace and prepare to print more addresses.  */
  442.       s = breakpos;
  443.       while (*s == ' ' || *s == '\t') ++s;
  444.           if (*s != 0)
  445.         fputs ("\n\t", rem->handle);
  446.     }
  447.       putc ('\n', rem->handle);
  448.     }
  449.   return;
  450. }
  451.  
  452. #define mail_error error
  453.  
  454. void
  455. setup_files (the_list, field)
  456.      register line_list the_list;
  457.      register char *field;
  458. {
  459.   register char *start;
  460.   register char c;
  461.   while (true)
  462.     {
  463.       while (((c = *field) != '\0') &&
  464.          ((c == ' ') ||
  465.           (c == '\t') ||
  466.           (c == ',')))
  467.     field += 1;
  468.       if (c != '\0')
  469.     {
  470.       start = field;
  471.       while (((c = *field) != '\0') &&
  472.          (c != ' ') &&
  473.          (c != '\t') &&
  474.          (c != ','))
  475.         field += 1;
  476.       *field = '\0';
  477.       if (!open_a_file (start))
  478.         mail_error ("Could not open file %s", start);
  479.       *field = c;
  480.       if (c != '\0') continue;
  481.     }
  482.       if (the_list == ((line_list) NULL)) return;
  483.       field = the_list->string;
  484.       the_list = the_list->continuation;
  485.     }
  486. }
  487.  
  488. int
  489. args_size (the_header)
  490.      header the_header;
  491. {
  492.   register header old = the_header;
  493.   register line_list rem;
  494.   register int size = 0;
  495.   do
  496.     {
  497.       char *field;
  498.       register char *keyword = get_keyword (the_header->text->string, &field);
  499.       if ((strcmp (keyword, "TO") == 0) ||
  500.       (strcmp (keyword, "CC") == 0) ||
  501.       (strcmp (keyword, "BCC") == 0))
  502.     {
  503.       size += 1 + strlen (field);
  504.       for (rem = the_header->text->continuation;
  505.            rem != NIL;
  506.            rem = rem->continuation)
  507.         size += 1 + strlen (rem->string);
  508.     }
  509.       the_header = the_header->next;
  510.     } while (the_header != old);
  511.   return size;
  512. }
  513.  
  514. parse_header (the_header, where)
  515.      header the_header;
  516.      register char *where;
  517. {
  518.   register header old = the_header;
  519.   do
  520.     {
  521.       char *field;
  522.       register char *keyword = get_keyword (the_header->text->string, &field);
  523.       if (strcmp (keyword, "TO") == 0)
  524.     where = add_field (the_header->text->continuation, field, where);
  525.       else if (strcmp (keyword, "CC") == 0)
  526.     where = add_field (the_header->text->continuation, field, where);
  527.       else if (strcmp (keyword, "BCC") == 0)
  528.     {
  529.       where = add_field (the_header->text->continuation, field, where);
  530.       the_header->previous->next = the_header->next;
  531.       the_header->next->previous = the_header->previous;
  532.     }
  533.       else if (strcmp (keyword, "FCC") == 0)
  534.     setup_files (the_header->text->continuation, field);
  535.       the_header = the_header->next;
  536.     } while (the_header != old);
  537.   *where = '\0';
  538.   return;
  539. }
  540.     
  541. header
  542. read_header ()
  543. {
  544.   register header the_header = ((header) NULL);
  545.   register line_list *next_line = ((line_list *) NULL);
  546.  
  547.   init_linebuffer (&lb);
  548.  
  549.   do
  550.     {
  551.       long length;
  552.       register char *line;
  553.  
  554.       readline (&lb, stdin);
  555.       line = lb.buffer;
  556.       length = strlen (line);
  557.       if (length == 0) break;
  558.  
  559.       if (has_keyword (line))
  560.     {
  561.       register header old = the_header;
  562.       the_header = new_header ();
  563.       if (old == ((header) NULL))
  564.         {
  565.           the_header->next = the_header;
  566.           the_header->previous = the_header;
  567.         }
  568.       else
  569.         {
  570.           the_header->previous = old;
  571.           the_header->next = old->next;
  572.           old->next = the_header;
  573.         }
  574.       next_line = &(the_header->text);
  575.     }
  576.  
  577.       if (next_line == ((line_list *) NULL))
  578.     {
  579.       /* Not a valid header */
  580.       exit (1);
  581.     }
  582.       *next_line = new_list ();
  583.       (*next_line)->string = alloc_string (length);
  584.       strcpy (((*next_line)->string), line);
  585.       next_line = &((*next_line)->continuation);
  586.       *next_line = NIL;
  587.  
  588.     } while (true);
  589.  
  590.   return the_header->next;
  591. }
  592.  
  593. void
  594. write_header (the_header)
  595.      header the_header;
  596. {
  597.   register header old = the_header;
  598.   do
  599.     {
  600.       register line_list the_list;
  601.       for (the_list = the_header->text;
  602.        the_list != NIL;
  603.        the_list = the_list->continuation)
  604.     put_line (the_list->string);
  605.       the_header = the_header->next;
  606.     } while (the_header != old);
  607.   put_line ("");
  608.   return;
  609. }
  610.  
  611. void
  612. main (argc, argv)
  613.      int argc;
  614.      char **argv;
  615. {
  616.   char *command_line;
  617.   header the_header;
  618.   long name_length;
  619.   char *mail_program_name;
  620.   char buf[BUFLEN + 1];
  621.   register int size;
  622.   FILE *the_pipe;
  623.  
  624.   extern char *getenv ();
  625.  
  626.   mail_program_name = getenv ("FAKEMAILER");
  627.   if (!(mail_program_name && *mail_program_name))
  628.     mail_program_name = MAIL_PROGRAM_NAME;
  629.   name_length = strlen (mail_program_name);
  630.  
  631.   my_name = MY_NAME;
  632.   the_streams = ((stream_list) NULL);
  633.   the_date = ((char *) NULL);
  634.   the_user = ((char *) NULL);
  635.  
  636.   the_header = read_header ();
  637.   command_line = alloc_string (name_length + args_size (the_header));
  638.   strcpy (command_line, mail_program_name);
  639.   parse_header (the_header, &command_line[name_length]);
  640.   
  641.   the_pipe = popen (command_line, "w");
  642.   if (the_pipe == ((FILE *) NULL))
  643.     fatal ("cannot open pipe to real mailer");
  644.  
  645.   add_a_stream (the_pipe, pclose);
  646.  
  647.   write_header (the_header);
  648.  
  649.   /* Dump the message itself */
  650.  
  651.   while (!feof (stdin))
  652.     {
  653.       size = fread (buf, 1, BUFLEN, stdin);
  654.       buf[size] = '\0';
  655.       put_string (buf);
  656.     }
  657.  
  658.   exit (close_the_streams ());
  659. }
  660.  
  661. #endif /* not BSD 4.2 (or newer) */
  662.